package cn.hsw.platform.core.doc.util;

import cn.hsw.platform.core.api.ManageApi;
import cn.hsw.platform.core.doc.contants.AuthConstant;
import cn.hsw.platform.core.doc.dto.AuthUserDto;
import cn.hsw.platform.core.doc.exception.CheckPermissionException;
import cn.hsw.platform.core.doc.exception.NotFoundException;
import cn.hsw.platform.core.dto.ResultDto;
import cn.hsw.platform.core.exception.ApiException;
import cn.hsw.platform.core.redis.RedisUtil;
import cn.hsw.platform.core.util.StrUtil;
import cn.hsw.platform.core.util.Validator;
import cn.hutool.core.util.IdUtil;
import com.alibaba.fastjson.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.Set;

/**
 * 权限工具
 * @author 李子木
 * @date 2020-12-04
 */
@Component
public class AuthUtil {
    @Autowired
    RedisUtil redisUtil;

    @Autowired
    ManageApi manageApi;

    /**
     * 是否需要鉴权
     * @param applicationName 路由命中的服务名
     * @return true需要 false 不需要
     */
    public boolean needAuth(String applicationName){
        return redisUtil.sHasKey(AuthConstant.AUTH_AUTHENTICATION_LIST, applicationName);
    }

    /**
     * 获取当前请求的权限code
     * @param applicationName 服务名
     * @param path 请求路径
     * @return code 或"" 空不需要鉴权
     */
    public String getTargetCode(String applicationName,String path){
        Object code = redisUtil.hget(AuthConstant.AUTH_PREFIX + applicationName, path);
        if(Validator.isEmpty(code)){
            Set<Object> hKeys = redisUtil.hKeys(AuthConstant.AUTH_PREFIX + applicationName);

            for (Object obj : hKeys) {
                if (Validator.isEmpty(obj)) {
                    continue;
                }
                String str = StrUtil.objToString(obj);
                if (!StrUtil.contains(str, AuthConstant.AUTH_URL_PATTERN)) {
                    continue;
                }
                //path与str做正则匹配
                if (Validator.isMactchRegex(str, path)) {

                    code = redisUtil.hget(AuthConstant.AUTH_PREFIX + applicationName, str);
                    System.out.println(path+"----"+str+"----"+code);
                    return StrUtil.objToString(code);
                }
            }

            throw new NotFoundException();
        }
        if(Validator.equal(code,AuthConstant.NO_NEED_AUTHENTICATION_DEFAULT_CODE)){
            return AuthConstant.NO_NEED_AUTHENTICATION_DEFAULT_CODE;
        }else{
            return StrUtil.objToString(code);
        }
    }

    /**
     * 根据 token获取auth用户信息
     * @param tokenList token列表
     * @return auth_user_dto
     */
    public AuthUserDto getAuthUser(List<String> tokenList){
        if(Validator.isNull(tokenList)){
            throw new CheckPermissionException("token不存在");
        }
        if(!Validator.equal(1,tokenList.size())){
            throw new CheckPermissionException("token信息非法");
        }
        if(!tokenList.get(0).toUpperCase().startsWith(AuthConstant.AUTH_TOKEN_PREFIX)){
            //token非法
            throw new CheckPermissionException("token前缀非法");
        }
        String token = tokenList.get(0).substring(7);
        if(Validator.equal("null",token)||Validator.isEmpty(token)){
            throw new CheckPermissionException("token内容非法");
        }
        //获取用户pid
        String userPid = StrUtil.objToString(redisUtil.get(AuthConstant.AUTH_USER_TOKEN_PREFIX + token));

        if(Validator.isEmpty(userPid)){
            throw new CheckPermissionException("token不存在或已过期");
        }
        AuthUserDto authUserDto = getAuthUserDto(Long.parseLong(userPid));
        //用户token验证成功，增加token时间
        tokenExpireTimeExpand(userPid,token);
        return authUserDto;
    }

    /**
     * 权限判断
     * @param rolePidList 角色ID列表
     * @param code 权限名称
     */
    public void authentication(List<Long> rolePidList, String code){
        for (int i = 0; i < rolePidList.size(); i++) {
            Long rolePid = rolePidList.get(i);

            if(redisUtil.hasKey(AuthConstant.AUTH_ROLE_CODE_SET_PREFIX + rolePid)){
                if(hasAuth(rolePid,code)){
                    return;
                }
            }else{

                synchronized (rolePid){
                    ResultDto<List<String>> resultDto = manageApi.authRole(rolePid);
                    if(resultDto.isSuccess()){
                        setAuthRoleCache(rolePid,resultDto.getData());
                    }
                }
                if(hasAuth(rolePid,code)){
                    return;
                }
            }

        }
        throw new CheckPermissionException("用户无权限");
    }

    /**
     * 登录
     * @param pid 用户主键ID
     * @param rolePidList 角色列表
     * @return -
     * @deprecated 感觉没有什么用废弃了
     */
    @Deprecated
    public String login(Long pid,List<Long> rolePidList){

        AuthUserDto authUserDto = new AuthUserDto();
        authUserDto.setPid(pid);
        authUserDto.setRolePidList(rolePidList);
        if(!setAuthUserCache(authUserDto.getPid(),authUserDto)){
            throw new CheckPermissionException("登录失败");
        }
        return getToken(pid);
    }

    /**
     * 用户登录方法
     * @param pid 用户主键
     * @return token
     */
    public String login(Long pid){
        return getToken(pid);
    }

    /**
     * 判断是否有auth_user缓存
     * @param pid 用户主键
     * @return 缓存信息
     * @deprecated 没啥用了
     */
    @Deprecated
    public boolean authUserCache(Long pid){
        return redisUtil.hasKey(AuthConstant.AUTH_USER_PREFIX+pid);
    }

    /**
     * token暂时用 hutool的simple uuid 3个拼接
     * @param pid 用户pid
     * @return token
     */
    private String getToken(Long pid){
        String token = "";
        /*查看token是否存在*/
        if(redisUtil.hasKey(AuthConstant.AUTH_USER_ACCESS_TOKEN_PREFIX + pid)){
            /*存在则使用原来的token*/
            token = (String) redisUtil.get(AuthConstant.AUTH_USER_ACCESS_TOKEN_PREFIX+pid);
            if(Validator.isEmpty(token)){
                token = IdUtil.simpleUUID()+IdUtil.simpleUUID()+IdUtil.simpleUUID();
            }
        }else{
            token = IdUtil.simpleUUID()+IdUtil.simpleUUID()+IdUtil.simpleUUID();
        }

        redisUtil.set(AuthConstant.AUTH_USER_TOKEN_PREFIX+token,pid,AuthConstant.TOKEN_EXPIRE_TIME);
        redisUtil.set(AuthConstant.AUTH_USER_ACCESS_TOKEN_PREFIX+pid,token,AuthConstant.TOKEN_EXPIRE_TIME);

        return token;
    }

    /**
     * 用户token时间延长
     * @param pid 用户id
     * @param token token
     */
    private void tokenExpireTimeExpand(String pid,String token){
        redisUtil.set(AuthConstant.AUTH_USER_TOKEN_PREFIX+token,pid,AuthConstant.TOKEN_EXPIRE_TIME);
        redisUtil.set(AuthConstant.AUTH_USER_ACCESS_TOKEN_PREFIX+pid,token,AuthConstant.TOKEN_EXPIRE_TIME);
    }

    /**
     * 添加auth_user缓存
     * @param pid 用户ID
     * @param authUserDto auth_user模型
     * @return -
     */
    private boolean setAuthUserCache(Long pid,AuthUserDto authUserDto){
        return redisUtil.set(AuthConstant.AUTH_USER_PREFIX+pid,authUserDto,AuthConstant.TOKEN_EXPIRE_TIME);
    }


    /**
     * 添加auth role缓存
     * @param rolePid 角色id
     * @param roleCodeList 权限code 列表
     */
    public void setAuthRoleCache(Long rolePid, List<String> roleCodeList) {
        clearRolePidCache(rolePid);
        redisUtil.sSetAndTime(AuthConstant.AUTH_ROLE_CODE_SET_PREFIX+rolePid,AuthConstant.AUTH_ROLE_CODE_EXPIRE_TIME, roleCodeList.toArray());
    }

    /**
     * 是否有权限缓存
     * @param rolePid 角色ID
     * @param code 权限code
     * @return -
     */
    private boolean hasAuth(Long rolePid,String code){
        return redisUtil.sHasKey(AuthConstant.AUTH_ROLE_CODE_SET_PREFIX + rolePid, code);
    }

    /**
     * 清除用户相关的缓存,当用户信息变更时使用
     * 删除用户相关的token
     *         *   1、access_token:AuthConstant.AUTH_USER_ACCESS_TOKEN_PREFIX+pid
     *         *   2、user_token:AuthConstant.AUTH_USER_TOKEN_PREFIX+access_token
     *         *   3、auth_user:AuthConstant.AUTH_USER_PREFIX+pid
     * @param userPid 用户主键
     */
    public void clearUserPidCache(Long userPid){
        Object accessToken = redisUtil.get(AuthConstant.AUTH_USER_ACCESS_TOKEN_PREFIX + userPid);
        if(Validator.isNotEmpty(accessToken)){
            redisUtil.del(AuthConstant.AUTH_USER_ACCESS_TOKEN_PREFIX + userPid,AuthConstant.AUTH_USER_TOKEN_PREFIX+accessToken);
        }
        redisUtil.del(AuthConstant.AUTH_USER_PREFIX+userPid);
    }

    /**
     * 删除角色相关的token
     *          *   1、role_code:AuthConstant.AUTH_ROLE_CODE_SET_PREFIX + rolePid
     * @param rolePid 角色主键
     */
    public void clearRolePidCache(Long rolePid){
        redisUtil.del(AuthConstant.AUTH_ROLE_CODE_SET_PREFIX + rolePid);
    }

    /**
     * 删除所有角色iD
     */
    public void clearPermissionPidCache(){
        redisUtil.del(AuthConstant.AUTH_ROLE_CODE_SET_PREFIX+"*");
    }

    public AuthUserDto getAuthUserDto(Long userPid){
        //获取用户信息
        String authUserStr = StrUtil.objToString(redisUtil.get(AuthConstant.AUTH_USER_PREFIX + userPid));
        AuthUserDto authUserDto = null;
        if(Validator.isNotEmpty(authUserStr)){
            authUserDto = JSONObject.parseObject(authUserStr, AuthUserDto.class);
        }else{
            synchronized (userPid){
                ResultDto<List<Long>> resultDto = manageApi.authUser(userPid);
                if(resultDto.isSuccess()){
                    authUserDto = new AuthUserDto();
                    authUserDto.setPid(userPid);
                    authUserDto.setRolePidList(resultDto.getData());
                    setAuthUserCache(authUserDto.getPid(),authUserDto);
                }else{
                    throw new ApiException();
                }
            }
        }
        if(Validator.isNull(authUserDto)){
            throw new CheckPermissionException("用户不存在或用户状态非法");
        }
        return authUserDto;
    }
}
